//==================================================================
//  Copyright (C) 2006-2007  Davide Pasca
//
//  This library is free software; you can redistribute it and/or
//  modify it under the terms of the GNU Lesser General Public
//  License as published by the Free Software Foundation; either
//  version 2.1 of the License, or (at your option) any later version.
//
//  This library is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
//  Lesser General Public License for more details.
//
//  You should have received a copy of the GNU Lesser General Public
//  License along with this library; if not, write to the Free Software
//  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
//==================================================================
///
///
///
///
//==================================================================

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "psys.h"
#include "pnetlib_socksupport.h"
#include "pnetlib_httpfile.h"

//==================================================================
enum
{
	STATE_CREATED,
	STATE_CONNECTING,
	STATE_SENDING,
	STATE_RECEIVING,
	STATE_DONE
};

//==================================================================
HTTPFile::HTTPFile( const char *ipnamep, const char *urlp, int port ) :
	_status(STATE_CREATED),
	_urlp(NULL),
	_expected_size(0),
	_data_start_idx(-1)
{
	int	len;

	switch( _ocf.Connect( ipnamep, port ) )
	{
	case COM_ERR_NONE:		_status = STATE_CONNECTING;	break;
	case COM_ERR_CONNECTED:	_status = STATE_SENDING;	break;
	default:		
		//_ocf.End();
		throw "Connection failed";
	}

	len = strlen( urlp );
	_urlp = new char[ len + 1024 ];
	sprintf_s( _urlp,
				len + 1024,
				"GET %s HTTP/1.1\r\n"
				"Host: %s\r\n"
				"Accept: */*\r\n"
				"\r\n", urlp, ipnamep );

}

//==================================================================
HTTPFile::~HTTPFile()
{
	if ( _urlp )
		delete [] _urlp;

	//_ocf.End();
}

//==================================================================
int HTTPFile::Idle()
{
int		err = 0;
u_int	send_ticket;

	switch ( _status )
	{
	case STATE_CONNECTING:
		switch( _ocf.Idle() )
		{
		case COM_ERR_NONE:									break;
		case COM_ERR_CONNECTED:	_status = STATE_SENDING;	break;
		default: goto badexit;
		}
		break;

	case STATE_SENDING:
		if ( _ocf.Idle() < 0 )
			goto badexit;

		{
			int retval = _ocf.Send( _urlp, strlen(_urlp)+1, &send_ticket );

			switch( retval )
			{
			case COM_ERR_NONE:
					_status = 3;
					break;
			
			default:
					goto badexit;
			}
		}
		break;

	case STATE_RECEIVING:
		if ( handleReception() )
			goto badexit;

		break;

	case STATE_DONE:
		break;
	}

	return 0;

badexit:
	//_ocf.End();
	_status = STATE_DONE;
	return -1;
}

//==================================================================
int HTTPFile::handleReception()
{
	switch ( _ocf.Idle() )
	{
	case COM_ERR_GRACEFUL_DISCONNECT:
		if ( _ocf.IsINDataReady() )
			_status = STATE_DONE;
		break;

	case COM_ERR_NONE:
		break;

	default:
		return -1;
	}

	char	retstr[2048];
	_ocf.GetINDataStr( retstr, _countof(retstr) );


	if ( strlen( retstr ) > 0 )
	{
		// if we have the start data, then we already parsed the header
		if ( _data_start_idx == -1 )
		{
			// look for the data start marker
			char	*head_endp = strstr( retstr, "\r\n\r\n" );
			if ( head_endp )
			{
				// set the data start index
				_data_start_idx = head_endp - retstr + 4;

				// parse the header once to retrieve the data size
				char	*content_lengthp;
				char	*content_lengthp_end;

				content_lengthp = strstr( retstr, "Content-Length:" );
				if ( content_lengthp )
				{
					content_lengthp += strlen( "Content-Length:" );
					content_lengthp_end = strstr( content_lengthp, "\r\n" );

					if ( content_lengthp_end )
					{
						int	len = content_lengthp_end - content_lengthp;
						if PTRAP_FALSE( len < 255 )
						{
							char	buff[256];
							memcpy( buff, content_lengthp, len );
							buff[len] = 0;

							_expected_size = atoi( buff );
						}
					}			
				}
			}
		}
	}

	if ( _data_start_idx >= 0 )
		if ( (_ocf.GetINDataSize() - _data_start_idx) >= _expected_size )
			_status = STATE_DONE;

	return 0;
}

//==================================================================
bool HTTPFile::GetINData( PUtils::Memfile &out_indata )
{
	PUtils::Memfile	*mfp;
	bool	done = _ocf.GetINData( mfp );

	if ( mfp->GetData() && _data_start_idx <= mfp->GetDataSize() )
	{
		if PTRAP_FALSE( _data_start_idx >= 0 )
		{
			out_indata.SetupRead( (u_char *)mfp->GetData() + _data_start_idx,
								  mfp->GetDataSize() - _data_start_idx );
		}		
	}
	else
	{
		out_indata.SetupRead( 0, 0 );
	}

	return done || (_status == STATE_DONE);
}

//==================================================================
bool HTTPFile::GetINDataStr( char *out_strp, u_int out_str_cnt )
{
	PUtils::Memfile	mf;

	bool done = GetINData( mf );

	int	str_data_size = mf.GetDataSize();
	PASSERT( (str_data_size+1) <= out_str_cnt );

	if ( str_data_size )
	{
		memcpy( out_strp,
				mf.GetData(),
				str_data_size );
	}

	out_strp[ str_data_size ] = 0;

	return done;
}

//==================================================================
int HTTPFile::GetPercentageComplete() const
{
	if ( _expected_size > 0 )
	{
		return (_ocf.GetINDataSize()-_data_start_idx) * 100 / _expected_size;
	}
	else
		return 0;
}
